#pragma once

/*
 * 自定义序列化类型
 */


#include <cstdint>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>

#include "Serialize.h"

namespace raft::utils {
    class DataStream {
    public:
        enum DataType {
            BOOL = 0,
            CHAR,
            INT32,
            INT64,
            FLOAT,
            DOUBLE,
            STRING,
            VECTOR,
            LIST,
            MAP,
            SET,
            CUSTOM
        };

        enum ByteOrder {
            BigEndian,
            LittleEndian
        };

        DataStream();

        explicit DataStream(const std::string &data);

        ~DataStream() = default;

        void show() const;

        void write(const char *data, unsigned long long len);

        void write(bool value);

        void write(char value);

        void write(int32_t value);

        void write(int64_t value);

        void write(float value);

        void write(double value);

        void write(const char *value);

        void write(const std::string &value);

        void write(const Serialize &value);

        template<typename T>
        void write(const std::vector<T> &value);

        template<typename T>
        void write(const std::list<T> &value);

        template<typename K, typename V>
        void write(const std::map<K, V> &value);

        template<typename T>
        void write(const std::set<T> &value);

        template<typename T, typename... Args>
        void writeArgs(const T &head, const Args &...args);

        void writeArgs() {}

        bool read(char *data, int len);

        bool read(bool &value);

        bool read(char &value);

        bool read(int32_t &value);

        bool read(int64_t &value);

        bool read(float &value);

        bool read(double &value);

        bool read(std::string &value);

        bool read(Serialize &value);

        template<typename T>
        bool read(std::vector<T> &value);

        template<typename T>
        bool read(std::list<T> &value);

        template<typename K, typename V>
        bool read(std::map<K, V> &value);

        template<typename T>
        bool read(std::set<T> &value);

        template<typename T, typename... Args>
        bool readArgs(T &head, Args &...args);

        bool readArgs();

        [[nodiscard]] const char *data() const;

        [[nodiscard]] unsigned long long size() const;

        void clear();

        void reset();

        void save(const std::string &filename);

        void load(const std::string &filename);

        DataStream &operator<<(bool value);

        DataStream &operator<<(char value);

        DataStream &operator<<(int32_t value);

        DataStream &operator<<(int64_t value);

        DataStream &operator<<(float value);

        DataStream &operator<<(double value);

        DataStream &operator<<(const char *value);

        DataStream &operator<<(const std::string &value);

        DataStream &operator<<(const Serialize &value);

        template<typename T>
        DataStream &operator<<(const std::vector<T> &value);

        template<typename T>
        DataStream &operator<<(const std::list<T> &value);

        template<typename K, typename V>
        DataStream &operator<<(const std::map<K, V> &value);

        template<typename T>
        DataStream &operator<<(const std::set<T> &value);

        DataStream &operator>>(bool &value);

        DataStream &operator>>(char &value);

        DataStream &operator>>(int32_t &value);

        DataStream &operator>>(int64_t &value);

        DataStream &operator>>(float &value);

        DataStream &operator>>(double &value);

        DataStream &operator>>(std::string &value);

        DataStream &operator>>(Serialize &value);

        template<typename T>
        DataStream &operator>>(std::vector<T> &value);

        template<typename T>
        DataStream &operator>>(std::list<T> &value);

        template<typename K, typename V>
        DataStream &operator>>(std::map<K, V> &value);

        template<typename T>
        DataStream &operator>>(std::set<T> &value);

    private:
        void reserve(unsigned long long len);

        ByteOrder byteorder();

        std::vector<char> buffer_;
        int pos_;
        ByteOrder byteorder_;
    };
}// namespace raft::utils

template<typename T>
void raft::utils::DataStream::write(const std::vector<T> &value) {
    char type = DataType::VECTOR;
    write((char *) &type, sizeof(char));
    int len = value.size();
    write(len);
    for (int i = 0; i < len; i++) {
        write(value[i]);
    }
}

template<typename T>
void raft::utils::DataStream::write(const std::list<T> &value) {
    char type = DataType::LIST;
    write((char *) &type, sizeof(char));
    int len = value.size();
    write(len);
    for (auto it = value.begin(); it != value.end(); it++) {
        write((*it));
    }
}

template<typename K, typename V>
void raft::utils::DataStream::write(const std::map<K, V> &value) {
    char type = DataType::MAP;
    write((char *) &type, sizeof(char));
    int len = value.size();
    write(len);
    for (auto it = value.begin(); it != value.end(); it++) {
        write(it->first);
        write(it->second);
    }
}

template<typename T>
void raft::utils::DataStream::write(const std::set<T> &value) {
    char type = DataType::SET;
    write((char *) &type, sizeof(char));
    int len = value.size();
    write(len);
    for (auto it = value.begin(); it != value.end(); it++) {
        write(*it);
    }
}

template<typename T, typename... Args>
void raft::utils::DataStream::writeArgs(const T &head, const Args &...args) {
    write(head);
    writeArgs(args...);
}

template<typename T>
bool raft::utils::DataStream::read(std::vector<T> &value) {
    value.clear();
    if (buffer_[pos_] != DataType::VECTOR) {
        return false;
    }
    ++pos_;
    int len;
    read(len);
    for (int i = 0; i < len; i++) {
        T v;
        read(v);
        value.push_back(v);
    }
    return true;
}

template<typename T>
bool raft::utils::DataStream::read(std::list<T> &value) {
    value.clear();
    if (buffer_[pos_] != DataType::LIST) {
        return false;
    }
    ++pos_;
    int len;
    read(len);
    for (int i = 0; i < len; i++) {
        T v;
        read(v);
        value.push_back(v);
    }
    return true;
}

template<typename K, typename V>
bool raft::utils::DataStream::read(std::map<K, V> &value) {
    value.clear();
    if (buffer_[pos_] != DataType::MAP) {
        return false;
    }
    ++pos_;
    int len;
    read(len);
    for (int i = 0; i < len; i++) {
        K k;
        read(k);

        V v;
        read(v);
        value[k] = v;
    }
    return true;
}

template<typename T>
bool raft::utils::DataStream::read(std::set<T> &value) {
    value.clear();
    if (buffer_[pos_] != DataType::SET) {
        return false;
    }
    ++pos_;
    int len;
    read(len);
    for (int i = 0; i < len; i++) {
        T v;
        read(v);
        value.insert(v);
    }
    return true;
}

template<typename T, typename... Args>
bool raft::utils::DataStream::readArgs(T &head, Args &...args) {
    read(head);
    return readArgs(args...);
}

template<typename T>
raft::utils::DataStream &raft::utils::DataStream::operator<<(const std::vector<T> &value) {
    write(value);
    return *this;
}

template<typename K, typename V>
raft::utils::DataStream &raft::utils::DataStream::operator<<(const std::map<K, V> &value) {
    write(value);
    return *this;
}

template<typename T>
raft::utils::DataStream &raft::utils::DataStream::operator<<(const std::set<T> &value) {
    write(value);
    return *this;
}

template<typename T>
raft::utils::DataStream &raft::utils::DataStream::operator>>(std::vector<T> &value) {
    read(value);
    return *this;
}

template<typename T>
raft::utils::DataStream &raft::utils::DataStream::operator>>(std::list<T> &value) {
    read(value);
    return *this;
}

template<typename K, typename V>
raft::utils::DataStream &raft::utils::DataStream::operator>>(std::map<K, V> &value) {
    read(value);
    return *this;
}

template<typename T>
raft::utils::DataStream &raft::utils::DataStream::operator>>(std::set<T> &value) {
    read(value);
    return *this;
}
